package org.mythflow.activity;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mythflow.domain.Recording;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MythFlow extends ListActivity {

	private static final String TAG = "MythFlow";
	private static final String URL = "/json/tv/recorded";
	
	//private ProgressDialog progressDialog = null;
	private ArrayList<Recording> recordings = new ArrayList<Recording>();
	private RecordingAdapter adapter;

	private Resources res;
	
	/* (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        
        Log.i( TAG, "onCreate : enter" );
        
		setContentView( R.layout.programs );

		res = getResources();
		
		if( null != savedInstanceState ) {
			recordings = savedInstanceState.getParcelableArrayList( "recordings" );
		}
		
		this.adapter = new RecordingAdapter( this, R.layout.programs_row, recordings );
		setListAdapter( this.adapter );
		
        if( "".equals( Preferences.getAddress( getApplicationContext() ) ) ) {
        	AlertDialog.Builder builder = new AlertDialog.Builder( this );
        	builder.setMessage( getResources().getString( R.string.alert_title ) + "\n\n" + getResources().getString( R.string.alert_text ) )
        		   .setCancelable( true )
        		   .setPositiveButton( "Close", new DialogInterface.OnClickListener() {
        			   public void onClick(DialogInterface dialog, int id) {
        				   dialog.dismiss();
        			   }
        		   });
        	AlertDialog alert = builder.create();
        	alert.show();
        } else {
        	if( recordings.isEmpty() ) {
        		refresh();
        	}
        }
        
        Log.i( TAG, "onCreate : exit" );
    }

	/* (non-Javadoc)
	 * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
	 */
	@Override
	protected void onSaveInstanceState( Bundle outState ) {
        Log.i( TAG, "onSaveInstanceState : enter" );
        
       	outState.putParcelableArrayList( "recordings", recordings );
        
		super.onSaveInstanceState( outState );
        Log.i( TAG, "onSaveInstanceState : exit" );
	}

	/* (non-Javadoc)
     * @see android.app.ListActivity#onListItemClick(android.widget.ListView, android.view.View, int, long)
     */
    @Override
	protected void onListItemClick( ListView l, View v, int position, long id ) {
		super.onListItemClick( l, v, position, id );
		Log.i( TAG, "onListItemClick : enter" );

		Recording recording = getRecording( position );
		
		if( null != recording ) {
			if( Log.isLoggable( TAG, Log.INFO ) ) {
				Log.i( TAG, "onListItemClick : recording=" + recording.toString() );
			}
			
			Intent intent = new Intent( this, ProgramDetails.class );
			intent.putExtra( "recording", recording );
			startActivity( intent );
		}
		
		Log.i( TAG, "onListItemClick : exit" );
	}

    /* (non-Javadoc)
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onCreateOptionsMenu( Menu menu ) {
    	super.onCreateOptionsMenu( menu );
        Log.d( TAG, "onCreateOptionsMenu : enter" );
    	
    	MenuInflater inflater = getMenuInflater();
    	inflater.inflate( R.menu.menu, menu );
    	
        Log.d( TAG, "onCreateOptionsMenu : exit" );
    	return true;
    }

    /* (non-Javadoc)
     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
     */
    @Override
    public boolean onOptionsItemSelected( MenuItem item ) {
        Log.d( TAG, "onOptionsItemSelected : enter" );

        switch( item.getItemId() ) {
    		case R.id.settings:
    			startActivity( new Intent( this, Preferences.class ) );
    			return true;
    		case R.id.refresh:
    			refresh();
    			return true;
    		case R.id.about:
    			startActivity( new Intent( this, About.class ) );
    			return true;
    	}
    	
        Log.d( TAG, "onOptionsItemSelected : exit" );
    	return false;
    }

	/* (non-Javadoc)
	 * @see android.app.Activity#onPrepareOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onPrepareOptionsMenu( Menu menu ) {
        Log.d( TAG, "onPrepareOptionsMenu : enter" );
		
        if( !"".equals( Preferences.getAddress( getApplicationContext() ) ) ) {
        	Log.d( TAG, "onPrepareOptionsMenu : address has been set, enable refresh menu" );
        	
        	menu.findItem( R.id.refresh ).setEnabled( true );
        }
		
        Log.d( TAG, "onPrepareOptionsMenu : exit" );
		return super.onPrepareOptionsMenu( menu );
	}

	public void refresh() {
		Log.i( TAG, "refresh : enter" );

		// run async updates in threads if myth backend address has been defined
        if( !"".equals( Preferences.getAddress( getApplicationContext() ) ) ) {
    		Log.d( TAG, "onCreate : address has been set" );

    		new GetRecordingsTask().execute();
        }
		
		Log.i( TAG, "refresh : exit" );
	}

    private Recording getRecording( int position ) {
    	Log.d( TAG, "getProgram : enter" );
    	Log.d( TAG, "getProgram : exit" );
    	return (Recording) adapter.getItem( position );
    }
 
    private class RecordingAdapter extends ArrayAdapter<Recording> {

    	private ArrayList<Recording> items;

    	public RecordingAdapter( Context context, int textViewResourceId, ArrayList<Recording> items ) {
    		super( context, textViewResourceId, items );
    		Log.v( TAG, "RecordingAdapter : enter" );
    		
    		this.items = items;

    		Log.v( TAG, "RecordingAdapter : exit" );
    	}
    	
    	@Override
    	public View getView( int position, View convertView, ViewGroup parent ) {
    		Log.v( TAG, "RecordingAdapter.getView : enter" );

    		View view = convertView;
    		RecordingHolder holder = null;
    		
    		if( null == view ) {
        		Log.v( TAG, "RecordingAdapter.getView : view == null" );
        		
        		LayoutInflater inflater = getLayoutInflater();
        		view = inflater.inflate( R.layout.programs_row, parent, false );
        		holder = new RecordingHolder( view, res );
        		view.setTag( holder );
    		} else {
        		Log.v( TAG, "RecordingAdapter.getView : view != null" );

        		holder = (RecordingHolder) view.getTag();
    		}
    		
    		holder.populate( items.get( position ) );
    		
    		Log.v( TAG, "RecordingAdapter.getView : exit" );
    		return view;
    	}
    
    }
    
    static class RecordingHolder {
    	private TextView programTitle = null;
    	private TextView programSubTitle = null;
    	
    	private View row = null;
    	private Resources res = null;
    	
    	public RecordingHolder( View row, Resources res ) {
    		Log.v( TAG, "RecordingHolder : enter" );

    		this.row = row;
    		this.res = res;
    		
    		programTitle = (TextView) this.row.findViewById( R.id.program_toptext );
    		programSubTitle = (TextView) this.row.findViewById( R.id.program_bottomtext );

    		Log.v( TAG, "RecordingHolder : exit" );
    	}
    	
    	void populate( Recording recording ) {
    		Log.v( TAG, "RecordingHolder.populate : enter" );
    		
			programTitle.setText( recording.getStartTimeFormattedShort() + " : " + recording.getTitle() + ( recording.isCurrentlyRecording() ? " " + res.getString( R.string.message_recording ) : "" ) );
			programSubTitle.setText( recording.getSubTitle() );

    		Log.v( TAG, "RecordingHolder.populate : exit" );
    	}
    }

    private class GetRecordingsTask extends AsyncTask<Void, Void, Void> {

    	private static final String TAG = "GetRecordingsTask";
		private final ProgressDialog dialog = new ProgressDialog( MythFlow.this );
		
		/* (non-Javadoc)
		 * @see android.os.AsyncTask#onPreExecute()
		 */
		@Override
		protected void onPreExecute() {
			Log.i( TAG, "onPreExecute : enter" );

			dialog.setTitle( getResources().getString( R.string.message_please_wait ) );
			dialog.setMessage( getResources().getString( R.string.message_loading ) );
			dialog.show();

			Log.i( TAG, "onPreExecute : exit" );
		}

		@Override
		protected Void doInBackground( Void... arg0 ) {
			Log.i( TAG, "doInBackground : enter" );

			HttpURLConnection con = null;
			try {
	        	String url = "http://" + Preferences.getAddress( getApplicationContext() ) + ":" + Preferences.getWebPort( getApplicationContext() ) + "/" + Preferences.getMythwebContext( getApplicationContext() ) + URL;
				Log.d( TAG, "doInBackground : url=" + url );
				
				if( !"".equals( Preferences.getMythwebUsername( getApplicationContext() ) ) && !"".equals( Preferences.getMythwebPassword( getApplicationContext() ) ) ) {
					Log.d( TAG, "doInBackground : setting up authentication" );
					Authenticator.setDefault( new MyAuthenticator( Preferences.getMythwebUsername( getApplicationContext() ), Preferences.getMythwebPassword( getApplicationContext() ) ) );
				}
				
				Log.d( TAG, "doInBackground : setting up url" );
				URL recordedUrl = new URL( url );
				
				Log.d( TAG, "doInBackground : opening connection" );
				con = (HttpURLConnection) recordedUrl.openConnection();
				con.setReadTimeout( 20000 );
				con.setUseCaches( false );
				con.setRequestMethod( "GET" );
				
				con.setDoInput( true );
				
				// Start the query
				Log.d( TAG, "doInBackground : connect" );
				con.connect();
				
				// Read results from the query
				Log.d( TAG, "doInBackground : setting up reader" );
				InputStream in = con.getInputStream();
				BufferedReader reader = new BufferedReader( new InputStreamReader( in, "UTF-8" ) );
				String payload = reader.readLine();
				reader.close();
				Log.d( TAG, "doInBackground : read data" );

				// Parse to get translated text
				Log.d( TAG, "doInBackground : creating JSONArray from payload" );
				recordings = new ArrayList<Recording>();
				JSONArray jsonArray = new JSONObject( payload ).getJSONArray( "recorded" );
				for( int i = 0; i < jsonArray.length(); i++ ) {
					Log.d( TAG, "doInBackground : recording iteration " + i );
					JSONObject json = jsonArray.getJSONObject( i );
					Recording recording = new Recording();
					recording.setTitle( json.getString( "title" ) );
					recording.setSubTitle( json.getString( "subtitle" ) );
					recording.setDescription( json.getString( "description" ) );
					recording.setChannelId( json.getString( "chanid") );
					recording.setStartTime( json.getString( "starttime" ) );
					recording.setEndTime( json.getString( "endtime" ) );
					recording.setLength( json.getString( "length" ) );
					recording.setUrl( json.getString( "url" ) );
					recordings.add( recording );
				}
				Log.d( TAG, "doInBackground : created " + recordings.size() + " recording entries" );
				
			} catch( MalformedURLException e ) {
				Log.e( TAG, "doInBackground : MalformedURLException", e );
			} catch( IOException e ) {
				Log.e( TAG, "doInBackground : IOException", e );
			} catch( JSONException e ) {
				Log.e( TAG, "doInBackground : JSONException", e );
			} finally {
				if( null != con ) {
					con.disconnect();
				}
			}

			Log.i( TAG, "doInBackground : exit" );
			return null;
		}

		/* (non-Javadoc)
		 * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
		 */
		@Override
		protected void onPostExecute( final Void unused ) {
			Log.i( TAG, "onPostExecute : enter" );

			if( !recordings.isEmpty() ) {
				Log.d( TAG, "onPostExecute : programs found" );

				adapter.clear();
				adapter.notifyDataSetChanged();
				    
				for( Recording recording : recordings ) {
					if( Log.isLoggable( TAG, Log.VERBOSE ) ) {
							Log.v( TAG, "onPostExecute : recording=" + recording.toString() );
					}

					adapter.add( recording );
			    }
			}
            
            adapter.notifyDataSetChanged();

            if( dialog.isShowing() ) {
				dialog.dismiss();
			}

			Log.i( TAG, "onPostExecute : exit" );
		}

    }
   
    private static class MyAuthenticator extends Authenticator {
    	
    	private String username;
    	private String password;
    	
    	public MyAuthenticator( String username, String password ) {
    		this.username = username;
    		this.password = password;
    	}
    	
        public PasswordAuthentication getPasswordAuthentication() {
            //Log.v( TAG, "MyAuthenticator : Feeding username and password for " + getRequestingScheme() );
            return new PasswordAuthentication( username, password.toCharArray() );
        }
    }

}